#include "designerworkbench.h"
#include "mainwindow.h"
#include "designer_actions.h"
#include "designer_toolwindow.h"
#include "designer_settings.h"
#include "qdesigner.h"
#include "designer_formwindow.h"
#include "project.h"
#include "settings.h"
#include "scripteditordialog.h"

#include <QtDesigner/QDesignerFormEditorInterface>

#include <QtDesigner/QDesignerFormEditorInterface>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtDesigner/QDesignerFormWindowManagerInterface>
#include <QtDesigner/QDesignerFormEditorPluginInterface>
#include <QtDesigner/QDesignerWidgetBoxInterface>
#include <QtDesigner/QDesignerMetaDataBaseInterface>

#include <QtDesigner/QDesignerComponents>
#include <qdesigner_integration_p.h>
#include <pluginmanager_p.h>
#include <formwindowbase_p.h>
#include <actioneditor_p.h>

#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QUrl>
#include <QtCore/QTimer>
#include <QtCore/QPluginLoader>
#include <QtCore/qdebug.h>

#include <QtGui/QActionGroup>
#include <QtGui/QCloseEvent>
#include <QtGui/QDesktopWidget>
#include <QtGui/QDockWidget>
#include <QtGui/QMenu>
#include <QtGui/QMenuBar>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
#include <QtGui/QToolBar>
#include <QtGui/QMdiArea>
#include <QtGui/QMdiSubWindow>
#include <QtGui/QLayout>
#include <QApplication>
#include <QProcess>

QDesignerWorkbench* QDesignerWorkbench::m_self = 0;

typedef QList<QAction *> ActionList;


static QMdiSubWindow *mdiSubWindowOf(const QWidget *w)
{
    QMdiSubWindow *rc = qobject_cast<QMdiSubWindow *>(w->parentWidget());
    Q_ASSERT(rc);
    return rc;
}

static inline void addActionsToMenu(QMenu *m, const ActionList &al)
{
    const ActionList::const_iterator cend = al.constEnd();
    for (ActionList::const_iterator it = al.constBegin(); it != cend; ++it){
        m->addAction(*it);
    }
}

static inline QMenu *addMenu(QMenuBar *mb, const QString &title, const ActionList &al)
{
    QMenu *rc = mb->addMenu(title);
    addActionsToMenu(rc, al);
    return rc;
}

// -------- QDesignerWorkbench

QDesignerWorkbench::QDesignerWorkbench() :   
    m_core(QDesignerComponents::createFormEditor(this)),
    m_globalMenuBar(new QMenuBar),
    m_windowActions(new QActionGroup(this)),
    m_project(0),    
    m_settings(Settings::instance()),
    m_scripterProcess(0),
    m_activeFormWindow(0)
{
    Q_ASSERT(m_core != 0);
    m_self = this;

    // TODO: remove 1 debug line bellow
    //m_project = new Project("c:/QtMobileScript/testProject1");

    QDesignerComponents::createTaskMenu(m_core, this);

    initializeCorePlugins();
    QDesignerComponents::initializePlugins(m_core);

    m_actionManager = new QDesignerActions(this);

    m_windowActions->setExclusive(true);
    connect(m_windowActions, SIGNAL(triggered(QAction*)), this, SLOT(formWindowActionTriggered(QAction*)));

    // Build main menu bar
    addMenu(m_globalMenuBar, tr("&File"), m_actionManager->fileActions()->actions());


    QMenu *editMenu = addMenu(m_globalMenuBar, tr("Edit"), m_actionManager->editActions()->actions());
    editMenu->addSeparator();
    addActionsToMenu(editMenu, m_actionManager->toolActions()->actions());

    addMenu(m_globalMenuBar, tr("F&orm"),  m_actionManager->formActions()->actions());

    QMenu *viewMenu = m_globalMenuBar->addMenu(tr("&View"));

    m_windowMenu = addMenu(m_globalMenuBar, tr("&Window"), m_actionManager->windowActions()->actions());

    addMenu(m_globalMenuBar, tr("&Project"), m_actionManager->projectActions()->actions());

    //  Add the tools in view menu order
    QActionGroup *viewActions = new QActionGroup(this);
    viewActions->setExclusive(false);

    for (int i = 0; i < QDesignerToolWindow::StandardToolWindowCount; i++) {
        QDesignerToolWindow *toolWindow = QDesignerToolWindow::createStandardToolWindow(static_cast< QDesignerToolWindow::StandardToolWindow>(i), this);        
        m_toolWindows.push_back(toolWindow);

        //toolWindow->show();
        if (QAction *action = toolWindow->action()) {
            viewMenu->addAction(action);
            viewActions->addAction(action);
        }
    }
    // Integration
    m_integration = new qdesigner_internal::QDesignerIntegration(m_core, this);
    connect(m_integration,SIGNAL(navigateToSlot(QString,QString,QStringList)),this,SLOT(navigateToSlot(QString,QString,QStringList)));

    // remaining view options (config toolbars)
    viewMenu->addSeparator();
    m_toolbarMenu = viewMenu->addMenu(tr("Toolbars"));

#ifndef Q_WS_MAC
    QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow();
    widgetBoxWrapper->action()->setVisible(true);
    widgetBoxWrapper->setWindowTitle(tr("Widget Box"));
#endif

    // Main Window creating
    const QDesignerSettings settings(m_core);
    m_dockedMainWindow = new DockedMainWindow(this, m_toolbarMenu, m_toolWindows);
    m_dockedMainWindow->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal);
    connect(m_dockedMainWindow, SIGNAL(closeEventReceived(QCloseEvent*)), this, SLOT(handleCloseEvent(QCloseEvent*)));
    //connect(m_dockedMainWindow, SIGNAL(formWindowActivated(QDesignerFormWindow*)), this, SLOT(slotFormWindowActivated(QDesignerFormWindow*)));
    connect(m_dockedMainWindow, SIGNAL(formWindowActivated(QDesignerFormWindow*)), this, SLOT(slotFormWindowActivated(QDesignerFormWindow*)));
    m_dockedMainWindow->restoreSettings(settings, m_dockedMainWindow->addToolWindows(m_toolWindows), desktopGeometry());

#ifndef Q_WS_MAC
    m_dockedMainWindow->setMenuBar(m_globalMenuBar);
    m_globalMenuBar->show();
#endif
    qDesigner->setMainWindow(m_dockedMainWindow);

//    foreach (QDesignerFormWindow *fw, m_formWindows) {
//        QMdiSubWindow *subwin = m_dockedMainWindow->createMdiSubWindow(fw, magicalWindowFlags(fw),
//                                                                       m_actionManager->closeFormAction()->shortcut());
//        subwin->hide();
//        if (QWidget *mainContainer = fw->editor()->mainContainer())
//            resizeForm(fw, mainContainer);
//    }

    //m_actionManager->setBringAllToFrontVisible(false);


    m_core->setTopLevel(m_dockedMainWindow);

    m_integration->updateCustomWidgetPlugins();
    m_dockedMainWindow->show();
    emit initialized();    
}

QDesignerWorkbench::~QDesignerWorkbench()
{
    m_core = 0;
    if(m_scripterProcess && m_scripterProcess->state() == QProcess::Running)
        m_scripterProcess->close();
}

QDesignerWorkbench* QDesignerWorkbench::instance()
{
    if(m_self == 0)
    {
        m_self = new QDesignerWorkbench();
    }
    return m_self;
}

void QDesignerWorkbench::destroy()
{ if(m_self!=0) delete m_self; m_self=0; }

QDesignerFormWindow * QDesignerWorkbench::openForm(const QString &fileName, QString *errorMessage)
{
    bool uic3Converted;
    QDesignerFormWindow *rc =loadForm(fileName, true, &uic3Converted, errorMessage);
    if (!rc)
        return 0;

    if (!uic3Converted)
        rc->editor()->setFileName(fileName);
    rc->firstShow();
    return rc;
}

QDesignerFormWindow * QDesignerWorkbench::openTemplate(const QString &templateFileName,
                                                       const QString &editorFileName,
                                                       QString *errorMessage)
{
    bool uic3Converted;
    QDesignerFormWindow *rc =loadForm(templateFileName, false, &uic3Converted, errorMessage);

    qDebug()<<rc;
    if (!rc)
        return 0;

    if (!uic3Converted)
        rc->editor()->setFileName(editorFileName);

    rc->firstShow();
    return rc;
}

int QDesignerWorkbench::formWindowCount() const
{
    return m_formWindows.count();
}

QDesignerFormWindow *QDesignerWorkbench::formWindow(int index) const
{
    return m_formWindows.at(index);
}


// Load a form or return 0 and do cleanup. file name and editor file
// name in case of loading a template file.

QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName,
                                                   bool detectLineTermiantorMode,
                                                   bool *uic3Converted,
                                                   QString *errorMessage)
{
    QFile file(fileName);

    qdesigner_internal::FormWindowBase::LineTerminatorMode mode = qdesigner_internal::FormWindowBase::NativeLineTerminator;

    if (detectLineTermiantorMode) {
        if (file.open(QFile::ReadOnly)) {
            const QString text = QString::fromUtf8(file.readLine());
            file.close();

            const int lf = text.indexOf(QLatin1Char('\n'));
            if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) {
                mode = qdesigner_internal::FormWindowBase::CRLFLineTerminator;
            } else if (lf >= 0) {
                mode = qdesigner_internal::FormWindowBase::LFLineTerminator;
            }
        }
    }

    if (!file.open(QFile::ReadOnly|QFile::Text)) {
        *errorMessage = tr("The file <b>%1</b> could not be opened.").arg(file.fileName());
        return 0;
    }


    // Create a form
    QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager();

    QDesignerFormWindow *formWindow = new QDesignerFormWindow(/*formWindow=*/ 0, this);
    addFormWindow(formWindow);
    //formWindow->installEventFilter(m_dockedMainWindow);
    QDesignerFormWindowInterface *editor = formWindow->editor();
    Q_ASSERT(editor);

    // Temporarily set the file name. It is needed when converting a UIC 3 file.
    // In this case, the file name will we be cleared on return to force a save box.
    editor->setFileName(fileName);
    editor->setContents(&file);

    if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(editor))
        fwb->setLineTerminatorMode(mode);


    QMdiSubWindow *subWin = m_dockedMainWindow->createMdiSubWindow(formWindow, m_actionManager->closeFormAction()->shortcut());
    m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWin);
    //subWin->setWindowTitle("TASDASD");
//    QPushButton *pb = new QPushButton(subWin);
//    pb->show();

    if (!editor->mainContainer()) {
        removeFormWindow(formWindow);
        formWindowManager->removeFormWindow(editor);
        m_core->metaDataBase()->remove(editor);
        *errorMessage = tr("The file <b>%1</b> is not a valid Designer UI file.").arg(file.fileName());
        return 0;
    }
    *uic3Converted = editor->fileName().isEmpty();
    editor->setDirty(false);
    resizeForm(formWindow, editor->mainContainer());
    formWindowManager->setActiveFormWindow(editor);
    return formWindow;
}

void QDesignerWorkbench::resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const
{
    const QSize containerSize = mainContainer->size();
    //const QSize containerMinimumSize = mainContainer->minimumSize();
    const QSize containerMaximumSize = mainContainer->maximumSize();
    // get decorations and resize MDI
    QMdiSubWindow *mdiSubWindow = qobject_cast<QMdiSubWindow *>(fw->parent());
    Q_ASSERT(mdiSubWindow);
    const QSize decorationSize = mdiSubWindow->geometry().size() - mdiSubWindow->contentsRect().size();
    mdiSubWindow->resize(containerSize + decorationSize);
    // In Qt::RightToLeft mode, the window can grow to be partially hidden by the right border.
    const int mdiAreaWidth = m_dockedMainWindow->mdiArea()->width();
    if (qApp->layoutDirection() == Qt::RightToLeft && mdiSubWindow->geometry().right() >= mdiAreaWidth)
        mdiSubWindow->move(mdiAreaWidth - mdiSubWindow->width(), mdiSubWindow->pos().y());

    if (containerMaximumSize == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) {
        mdiSubWindow->setMaximumSize(containerMaximumSize);
    } else {
        mdiSubWindow->setMaximumSize(containerMaximumSize + decorationSize);
    }
}

QRect QDesignerWorkbench::desktopGeometry() const
{
    // Return geometry of the desktop designer is running in.
    QWidget *widget = 0;
    widget = m_dockedMainWindow;
    const QDesktopWidget *desktop = qApp->desktop();
    const int screenNumber = widget ? desktop->screenNumber(widget) : 0;
    return desktop->availableGeometry(screenNumber);
}

QRect QDesignerWorkbench::availableGeometry() const
{
    return m_dockedMainWindow->mdiArea()->geometry();
}

QDesignerToolWindow *QDesignerWorkbench::widgetBoxToolWindow() const
{
    return m_toolWindows.at(QDesignerToolWindow::WidgetBox);
}

void QDesignerWorkbench::handleCloseEvent(QCloseEvent *ev)
{
    ev->setAccepted(handleClose());
    if (ev->isAccepted())
        QMetaObject::invokeMethod(qDesigner, "quit", Qt::QueuedConnection);  // We're going down!
}

bool QDesignerWorkbench::handleClose()
{
    if(m_project==0) return true;

//    QMessageBox box(QMessageBox::Warning, tr("Exit?"),
//            tr("Do you want exit right now?"),
//            QMessageBox::Yes | QMessageBox::No);
//        box.button(QMessageBox::Yes)->setText(tr("Go away"));
//    QPushButton *notnow = static_cast<QPushButton *>(box.button(QMessageBox::No));
//    notnow->setText(tr("Not now"));
//    box.setDefaultButton(notnow);
//    switch (box.exec()) {
//    case QMessageBox::No:
//        return false;
//    case QMessageBox::Yes:

//       break;
//    }
    if(closeProject())
        saveSettings();
    else
        return false;

    return true;
}

void QDesignerWorkbench::saveSettings() const
{
    QDesignerSettings settings(m_core);
    settings.clearBackup();
    m_dockedMainWindow->saveSettings(settings);
    //settings.setValue("projectPath",m_project->path());
    //AppFontWidget::save(m_core->settingsManager(), QLatin1String(appFontPrefixC));
}

void QDesignerWorkbench::addFormWindow(QDesignerFormWindow *formWindow)
{
    // ### Q_ASSERT(formWindow->windowTitle().isEmpty() == false);

    m_formWindows.append(formWindow);

    //m_actionManager->setWindowListSeparatorVisible(true);

    if (QAction *action = formWindow->action()) {
        m_windowActions->addAction(action);
        m_windowMenu->addAction(action);
        action->setChecked(true);
    }

    m_actionManager->minimizeAction()->setEnabled(true);
    m_actionManager->minimizeAction()->setChecked(false);
    connect(formWindow, SIGNAL(minimizationStateChanged(QDesignerFormWindowInterface*,bool)),
            this, SLOT(minimizationStateChanged(QDesignerFormWindowInterface*,bool)));

    m_actionManager->editWidgets()->trigger();
}
void QDesignerWorkbench::removeFormWindow(QDesignerFormWindow *formWindow)
{
    QDesignerFormWindowInterface *editor = formWindow->editor();
    const bool loadOk = editor->mainContainer();
    Q_UNUSED(loadOk);
    updateBackup(editor);
    const int index = m_formWindows.indexOf(formWindow);
    if (index != -1) {
        m_formWindows.removeAt(index);
    }

    if (QAction *action = formWindow->action()) {
        m_windowActions->removeAction(action);
        m_windowMenu->removeAction(action);
    }

    if (m_formWindows.empty()) {
        //m_actionManager->setWindowListSeparatorVisible(false);
        // Show up new form dialog unless closing
//        if (loadOk && m_state == StateUp
//            && QDesignerSettings(m_core).showNewFormOnStartup()) {
//            QTimer::singleShot(200, m_actionManager, SLOT(createForm()));
//        }
    }
}


bool QDesignerWorkbench::readInBackup()
{
    const QMap<QString, QString> backupFileMap = QDesignerSettings(m_core).backup();
    if (backupFileMap.isEmpty())
        return false;

    const  QMessageBox::StandardButton answer =
        QMessageBox::question(0, tr("Backup Information"),
                                 tr("The last session of Designer was not terminated correctly. "
                                       "Backup files were left behind. Do you want to load them?"),
                                    QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
    if (answer == QMessageBox::No)
        return false;

    const QString modifiedPlaceHolder = QLatin1String("[*]");
    QMapIterator<QString, QString> it(backupFileMap);
    while(it.hasNext()) {
        it.next();

        QString fileName = it.key();
        fileName.remove(modifiedPlaceHolder);

        if(m_actionManager->readInForm(it.value()))
            formWindowManager()->activeFormWindow()->setFileName(fileName);

    }
    return true;
}

void QDesignerWorkbench::updateBackup(QDesignerFormWindowInterface* fwi)
{
    QString fwn = QDir::convertSeparators(fwi->fileName());
    if (fwn.isEmpty())
        fwn = fwi->parentWidget()->windowTitle();

    QDesignerSettings settings(m_core);
    QMap<QString, QString> map = settings.backup();
    map.remove(fwn);
    settings.setBackup(map);
}

QDesignerFormWindowManagerInterface *QDesignerWorkbench::formWindowManager() const
{
    return m_core->formWindowManager();
}

bool QDesignerWorkbench::saveForm(QDesignerFormWindowInterface *frm)
{
    return m_actionManager->saveForm(frm);
}

bool QDesignerWorkbench::closeProject()
{
    if(m_project!=0) {
        int res = QMessageBox::question(0,tr("Close project"),"Save current project before closing?", QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
        if(res == QMessageBox::Yes || res == QMessageBox::No) {
            if(res == QMessageBox::Yes)
                saveProject();
            delete m_project;
            m_project = 0;
            m_actionManager->projectStateChanged();

            for(int i=0;i<m_core->formWindowManager()->formWindowCount() && res; i++) {
                QDesignerFormWindowInterface *fw = m_core->formWindowManager()->formWindow(i);
                if (QWidget *parent = fw->parentWidget()) {
                    if (QMdiSubWindow *mdiSubWindow = qobject_cast<QMdiSubWindow *>(parent->parentWidget())) {
                        mdiSubWindow->close();
                    } else {
                        parent->close();
                    }
                }
            }

            return true;
        }
    }
    return false;
}

bool QDesignerWorkbench::openProject(const QString &projectFile)
{
    bool res = false;
    closeProject();
    m_project = new Project(projectFile);
    res = m_project->read();
    return res;
}

bool QDesignerWorkbench::saveProject()
{
    bool res = m_project->write();

    for(int i=0;i<m_core->formWindowManager()->formWindowCount() && res; i++)
    {
        QDesignerFormWindowInterface *fw = m_core->formWindowManager()->formWindow(i);
        if(!saveForm(fw))
        {
            QMessageBox::critical(mainWindow(),tr("Error"),tr("Cannot save form %1").arg(fw->fileName()));
            res = false;
        }

    }

    return res;
}

void QDesignerWorkbench::initializeCorePlugins()
{
//    QDesignerPluginManager *pm = m_core->pluginManager();

    QList<QObject*> plugins = QPluginLoader::staticInstances();
    plugins += core()->pluginManager()->instances();

    foreach (QObject *plugin, plugins) {
        if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin)) {
            if (!formEditorPlugin->isInitialized())
                formEditorPlugin->initialize(core());
        }
    }
}

void QDesignerWorkbench::formWindowActionTriggered(QAction *a)
{
    QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(a->parentWidget());
    Q_ASSERT(fw);

    if (isFormWindowMinimized(fw))
        setFormWindowMinimized(fw, false);

    if (QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(fw->parent())) {
        m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWindow);
    }

}

bool QDesignerWorkbench::isFormWindowMinimized(const QDesignerFormWindow *fw)
{
    return mdiSubWindowOf(fw)->isShaded();
}

void QDesignerWorkbench::setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized)
{
    QMdiSubWindow *mdiSubWindow = mdiSubWindowOf(fw);
    if (minimized) {
        mdiSubWindow->showShaded();
    } else {
        mdiSubWindow->setWindowState(mdiSubWindow->windowState() & ~Qt::WindowMinimized);
    }
}

void QDesignerWorkbench::minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized)
{
    // refresh the minimize action state
    if (core()->formWindowManager()->activeFormWindow() == formWindow) {
        m_actionManager->minimizeAction()->setChecked(minimized);
    }
}

void QDesignerWorkbench::slotFormWindowActivated(QDesignerFormWindow* fw)
{
    m_core->formWindowManager()->setActiveFormWindow(fw->editor());
    m_activeFormWindow = fw;
}

void QDesignerWorkbench::toggleFormMinimizationState()
{
    QDesignerFormWindowInterface *fwi = core()->formWindowManager()->activeFormWindow();
    if (!fwi)
        return;
    QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(fwi->parentWidget());
    Q_ASSERT(fw);
    setFormWindowMinimized(fw, !isFormWindowMinimized(fw));
}

void QDesignerWorkbench::navigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList &parameterNames)
{
    if(m_activeFormWindow)
        m_activeFormWindow->editScriptFunction(objectName, signalSignature, parameterNames);    
}
#include "databasedialog.h"
void QDesignerWorkbench::projectDatabaseSlot()
{
    DatabaseDialog dlg;
    dlg.exec();
}
#include "projectsettings.h"
void QDesignerWorkbench::projectSettingsSlot()
{
    ProjectSettings dlg;
    dlg.exec();
    qDebug()<<"void QDesignerWorkbench::projectSettingsSlot()";
}

void QDesignerWorkbench::editProjecScriptSlot()
{
    ScriptEditorDialog dlg;
    dlg.setScriptText(m_project->script());

    if(dlg.exec() == QDialog::Accepted)
    {
        m_project->setScript( dlg.scriptText() );
    }
}

void QDesignerWorkbench::runProjectSlot()
{
    QString scripterPath = Settings::instance()->scripterPath();
    scripterPath.replace('/','\\');
    qDebug()<<"RUN Project"<<m_project->fileFullName()<<scripterPath;
    if(scripterPath.length() > 0 ) {
        QStringList params;
        params.append(m_project->fileFullName());
        //QProcess::execute(scripterPath, params);
        if(m_scripterProcess) {
            QProcess::ProcessState state = m_scripterProcess->state();
            switch(state) {
            case QProcess::NotRunning:
                break;
            case QProcess::Starting:
                m_scripterProcess->close();
                break;
            case QProcess::Running:
                m_scripterProcess->close();
                break;
            }
            delete m_scripterProcess;
        }
        m_scripterProcess = new QProcess(this);
        m_scripterProcess->start(scripterPath, params);
    }
    else
        QMessageBox::information(0,tr("Error"),tr("Setup scripter executable path in settings first!"));
}
